const express = require('express');
const https = require('https');
const http = require('http');
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const multer = require('multer');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const swaggerUi = require('swagger-ui-express');
const swaggerJsdoc = require('swagger-jsdoc');

// 常量定义
const SUCCESS_CODE = 200;

// ==================== Swagger 配置 ====================

// 动态生成服务器地址的函数
function getServers(host) {
  const hostname = host ? host.split(':')[0] : 'localhost';
  return [
    {
      url: `http://${hostname}:4000`,
      description: 'HTTP 服务器',
    },
    {
      url: `https://${hostname}:4001`,
      description: 'HTTPS 服务器（基础TLS）',
    },
    {
      url: `https://${hostname}:4002`,
      description: 'HTTPS 双向认证（带密码）',
    },
    {
      url: `https://${hostname}:4003`,
      description: 'HTTPS 双向认证（无密码）',
    },
    {
      url: `https://${hostname}:4004`,
      description: 'HTTPS（TLS 1.2）',
    },
    {
      url: `https://${hostname}:4005`,
      description: 'HTTP/2 服务器',
    },
  ];
}

const swaggerOptions = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'OHOS Axios Test Server API',
      version: '2.0.0',
      description: 'OpenHarmony HTTP/HTTPS 测试服务器 API 文档 - 支持多种 HTTP 方法、TLS 配置、文件操作和证书管理',
      contact: {
        name: 'OHOS Test Server',
      },
      license: {
        name: 'MIT',
        url: 'https://opensource.org/licenses/MIT',
      },
    },
    servers: getServers('localhost'),  // 默认值，会被动态替换
    tags: [
      {
        name: 'Basic',
        description: '基础 HTTP 请求操作',
      },
      {
        name: 'Files',
        description: '文件上传和下载',
      },
      {
        name: 'Certificates',
        description: '证书下载和管理',
      },
    ],
    components: {
      schemas: {
        SuccessResponse: {
          type: 'object',
          properties: {
            method: {
              type: 'string',
              example: 'GET',
            },
            message: {
              type: 'string',
              example: 'Request successful',
            },
            timestamp: {
              type: 'string',
              format: 'date-time',
              example: '2025-11-25T10:00:00.000Z',
            },
          },
        },
        ErrorResponse: {
          type: 'object',
          properties: {
            error: {
              type: 'string',
              example: 'Error message',
            },
            message: {
              type: 'string',
              example: 'Detailed error description',
            },
          },
        },
      },
    },
  },
  apis: ['./server.js'], // 扫描当前文件的注释
};

const swaggerSpec = swaggerJsdoc(swaggerOptions);

// 配置文件上传
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    const uploadDir = path.join(__dirname, 'upload');
    if (!fs.existsSync(uploadDir)) {
      fs.mkdirSync(uploadDir, { recursive: true });
    }
    cb(null, uploadDir);
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

const upload = multer({ storage: storage });

// 创建 Express 应用
const app = express();

// 中间件
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(morgan('combined'));
app.use('/public', express.static(path.join(__dirname, 'public')));

// 动态生成 Swagger Spec（根据请求的 host）
function getDynamicSwaggerSpec(host) {
  const spec = JSON.parse(JSON.stringify(swaggerSpec)); // 深拷贝
  spec.servers = getServers(host);
  return spec;
}

// Swagger UI - 使用动态配置
app.use('/api-docs', swaggerUi.serve);
app.get('/api-docs', (req, res, next) => {
  const dynamicSpec = getDynamicSwaggerSpec(req.headers.host);
  return swaggerUi.setup(dynamicSpec, {
    customCss: '.swagger-ui .topbar { display: none }',
    customSiteTitle: 'OHOS Test Server API',
  })(req, res, next);
});

// Swagger JSON - 动态返回
app.get('/api-docs.json', (req, res) => {
  const dynamicSpec = getDynamicSwaggerSpec(req.headers.host);
  res.setHeader('Content-Type', 'application/json');
  res.send(dynamicSpec);
});

// ==================== 路由定义 ====================

/**
 * @swagger
 * /:
 *   get:
 *     summary: 服务器主页
 *     description: 返回服务器信息和可用端点列表
 *     tags: [Basic]
 *     responses:
 *       200:
 *         description: HTML 格式的服务器信息页面
 *         content:
 *           text/html:
 *             schema:
 *               type: string
 */
// 主页
app.get('/', (req, res) => {
  res.send(`
    <html>
      <head><title>OHOS Axios Test Server</title></head>
      <body>
        <h1>🚀 OpenHarmony HTTP/HTTPS 测试服务器</h1>
        <h2>可用端点：</h2>
        <ul>
          <li><a href="/tpc/get">GET /tpc/get</a></li>
          <li>POST /tpc/post</li>
          <li>PUT /tpc/put</li>
          <li>PATCH /tpc/patch</li>
          <li>DELETE /tpc/delete</li>
          <li><a href="/tpc/getDelay">GET /tpc/getDelay</a> (8秒延迟)</li>
          <li><a href="/tpc/redirect">GET /tpc/redirect</a> (重定向)</li>
          <li><a href="/tpc/getGzip">GET /tpc/getGzip</a> (GZIP压缩响应)</li>
          <li>POST /tpc/postGzip (GZIP压缩响应)</li>
          <li>POST /tpc/upload (文件上传)</li>
          <li><a href="/tpc/download">GET /tpc/download</a> (下载第一个文件)</li>
          <li><a href="/upload">GET /upload</a> (列出已上传文件 JSON)</li>
          <li>GET /upload/:filename (下载指定文件)</li>
          <li><a href="/files">📁 文件管理页面</a> (上传/下载/删除)</li>
        </ul>
        <h2>📜 证书下载：</h2>
        <h4>🔓 无密码版本（用于 4003 端口）</h4>
        <ul>
          <li><a href="/cert/ca.crt">GET /cert/ca.crt</a> (CA根证书 - 信任服务器)</li>
          <li><a href="/cert/server.crt">GET /cert/server.crt</a> (服务器证书)</li>
          <li><a href="/cert/client.crt">GET /cert/client.crt</a> (客户端证书)</li>
          <li><a href="/cert/client.key">GET /cert/client.key</a> (客户端私钥 - 无密码)</li>
          <li><a href="/cert/client.p12">GET /cert/client.p12</a> (P12证书包 - 无密码)</li>
        </ul>
        <h4>🔐 带密码版本（用于 4002 端口，密码: 123456）</h4>
        <ul>
          <li><a href="/cert/client_encrypted.key">GET /cert/client_encrypted.key</a> (客户端私钥 - 密码: 123456)</li>
          <li><a href="/cert/client_encrypted.p12">GET /cert/client_encrypted.p12</a> (P12证书包 - 密码: 123456)</li>
        </ul>
        <h4>📦 其他</h4>
        <ul>
          <li><a href="/cert/client-bundle">GET /cert/client-bundle</a> (一键获取所有证书 JSON)</li>
          <li><a href="/cert/list">GET /cert/list</a> (证书列表和状态)</li>
        </ul>
        <p style="color: #666; font-size: 14px;">
          💡 提示：4002 端口需使用带密码的证书（client_encrypted.key 或 client_encrypted.p12）<br>
          💡 提示：4003 端口使用无密码的证书（client.key 或 client.p12）
        </p>
        <h2>📖 API 文档：</h2>
        <ul>
          <li><a href="/api-docs" target="_blank">🔗 Swagger UI 交互式文档</a></li>
          <li><a href="/api-docs.json" target="_blank">📄 OpenAPI JSON 规范</a></li>
        </ul>
        <h3>服务器信息：</h3>
        <ul>
          <li>HTTP 端口: 4000</li>
          <li>HTTPS 基础端口: 4001（单向认证）</li>
          <li>HTTPS 双向认证端口: 4002（使用 client_encrypted.key/p12，密码: 123456）</li>
          <li>HTTPS 双向认证端口: 4003（使用 client.key/p12，无密码）</li>
          <li>HTTPS TLS 1.2 端口: 4004</li>
          <li>HTTP/2 端口: 4005</li>
        </ul>
      </body>
    </html>
  `);
});

/**
 * @swagger
 * /tpc/get:
 *   get:
 *     summary: GET 请求测试
 *     description: 测试 GET 请求，返回查询参数和请求头
 *     tags: [Basic]
 *     parameters:
 *       - in: query
 *         name: param1
 *         schema:
 *           type: string
 *         description: 测试查询参数
 *     responses:
 *       200:
 *         description: 成功响应
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 method:
 *                   type: string
 *                   example: GET
 *                 message:
 *                   type: string
 *                   example: GET request successful
 *                 timestamp:
 *                   type: string
 *                   format: date-time
 *                 query:
 *                   type: object
 *                 headers:
 *                   type: object
 */
// GET 请求
app.get('/tpc/get', (req, res) => {
  res.json({
    method: 'GET',
    message: 'GET request successful',
    timestamp: new Date().toISOString(),
    query: req.query,
    headers: req.headers
  });
});

/**
 * @swagger
 * /tpc/post:
 *   post:
 *     summary: POST 请求测试
 *     description: 测试 POST 请求，返回请求体和请求头
 *     tags: [Basic]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               data:
 *                 type: string
 *                 example: test data
 *     responses:
 *       200:
 *         description: 成功响应
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/SuccessResponse'
 */
// POST 请求
app.post('/tpc/post', (req, res) => {
  res.json({
    method: 'POST',
    message: 'POST request successful',
    timestamp: new Date().toISOString(),
    body: req.body,
    headers: req.headers
  });
});

/**
 * @swagger
 * /tpc/put:
 *   put:
 *     summary: PUT 请求测试
 *     description: 测试 PUT 请求，返回请求体和请求头
 *     tags: [Basic]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *     responses:
 *       200:
 *         description: 成功响应
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/SuccessResponse'
 */
// PUT 请求
app.put('/tpc/put', (req, res) => {
  res.json({
    method: 'PUT',
    message: 'PUT request successful',
    timestamp: new Date().toISOString(),
    body: req.body,
    headers: req.headers
  });
});

/**
 * @swagger
 * /tpc/patch:
 *   patch:
 *     summary: PATCH 请求测试
 *     description: 测试 PATCH 请求，用于部分更新资源
 *     tags: [Basic]
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *     responses:
 *       200:
 *         description: 成功响应
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/SuccessResponse'
 */
// PATCH 请求
app.patch('/tpc/patch', (req, res) => {
  res.json({
    method: 'PATCH',
    message: 'PATCH request successful',
    timestamp: new Date().toISOString(),
    body: req.body,
    headers: req.headers
  });
});

/**
 * @swagger
 * /tpc/delete:
 *   delete:
 *     summary: DELETE 请求测试
 *     description: 测试 DELETE 请求，返回查询参数
 *     tags: [Basic]
 *     parameters:
 *       - in: query
 *         name: id
 *         schema:
 *           type: string
 *         description: 要删除的资源ID
 *     responses:
 *       200:
 *         description: 成功响应
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/SuccessResponse'
 */
// DELETE 请求
app.delete('/tpc/delete', (req, res) => {
  res.json({
    method: 'DELETE',
    message: 'DELETE request successful',
    timestamp: new Date().toISOString(),
    query: req.query,
    headers: req.headers
  });
});

/**
 * @swagger
 * /tpc/getDelay:
 *   get:
 *     summary: 延迟响应测试
 *     description: 测试延迟响应，8秒后返回结果（用于测试超时处理）
 *     tags: [Basic]
 *     responses:
 *       200:
 *         description: 延迟8秒后的成功响应
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 method:
 *                   type: string
 *                   example: GET
 *                 message:
 *                   type: string
 *                   example: Delayed response after 8 seconds
 *                 timestamp:
 *                   type: string
 *                   format: date-time
 *                 delay:
 *                   type: string
 *                   example: 8000ms
 */
// 延迟响应 (8秒)
app.get('/tpc/getDelay', (req, res) => {
  setTimeout(() => {
    res.json({
      method: 'GET',
      message: 'Delayed response after 8 seconds',
      timestamp: new Date().toISOString(),
      delay: '8000ms'
    });
  }, 8000);
});

/**
 * @swagger
 * /tpc/redirect:
 *   get:
 *     summary: 重定向测试
 *     description: 测试 HTTP 重定向，会重定向到 /tpc/get
 *     tags: [Basic]
 *     responses:
 *       302:
 *         description: 重定向到 /tpc/get
 */
// 重定向
app.get('/tpc/redirect', (req, res) => {
  res.redirect('/tpc/get');
});

/**
 * @swagger
 * /tpc/getGzip:
 *   get:
 *     summary: GZIP 压缩 GET 请求测试
 *     description: 返回经过 GZIP 压缩的 JSON 响应，用于测试客户端的 GZIP 解压能力
 *     tags: [Basic]
 *     responses:
 *       200:
 *         description: GZIP 压缩的 JSON 响应
 *         headers:
 *           Content-Encoding:
 *             schema:
 *               type: string
 *               example: gzip
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 code:
 *                   type: integer
 *                   example: 200
 *                 data:
 *                   type: string
 *                   example: 这是一个经过GZIP压缩的GET请求响应。
 *                 msg:
 *                   type: string
 *                   example: GZIP压缩成功
 */
// GZIP 压缩 GET 请求
app.get('/tpc/getGzip', (req, res) => {
  const data = {
    code: SUCCESS_CODE,
    data: '这是一个经过GZIP压缩的GET请求响应。',
    msg: 'GZIP压缩成功',
  };

  const jsonString = JSON.stringify(data);
  const inputBuffer = Buffer.from(jsonString, 'utf8');
  const compressedBuffer = zlib.gzipSync(inputBuffer);

  res.writeHead(200, {
    'Content-Type': 'application/json; charset=utf-8',
    'Content-Encoding': 'gzip',
    'Content-Length': compressedBuffer.length,
  });

  res.end(compressedBuffer);
});

/**
 * @swagger
 * /tpc/postGzip:
 *   post:
 *     summary: GZIP 压缩 POST 请求测试
 *     description: 返回经过 GZIP 压缩的 JSON 响应，用于测试客户端的 GZIP 解压能力
 *     tags: [Basic]
 *     requestBody:
 *       required: false
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *     responses:
 *       200:
 *         description: GZIP 压缩的 JSON 响应
 *         headers:
 *           Content-Encoding:
 *             schema:
 *               type: string
 *               example: gzip
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 code:
 *                   type: integer
 *                   example: 200
 *                 data:
 *                   type: string
 *                   example: 这是一个经过GZIP压缩的POST请求响应。
 *                 msg:
 *                   type: string
 *                   example: GZIP压缩成功
 */
// GZIP 压缩 POST 请求
app.post('/tpc/postGzip', (req, res) => {
  const data = {
    code: SUCCESS_CODE,
    data: '这是一个经过GZIP压缩的POST请求响应。',
    msg: 'GZIP压缩成功',
  };

  const jsonString = JSON.stringify(data);
  const inputBuffer = Buffer.from(jsonString, 'utf8');
  const compressedBuffer = zlib.gzipSync(inputBuffer);

  res.writeHead(200, {
    'Content-Type': 'application/json; charset=utf-8',
    'Content-Encoding': 'gzip',
    'Content-Length': compressedBuffer.length,
  });

  res.end(compressedBuffer);
});

/**
 * @swagger
 * /tpc/upload:
 *   post:
 *     summary: 文件上传
 *     description: 上传文件到服务器
 *     tags: [Files]
 *     requestBody:
 *       required: true
 *       content:
 *         multipart/form-data:
 *           schema:
 *             type: object
 *             properties:
 *               file:
 *                 type: string
 *                 format: binary
 *                 description: 要上传的文件
 *     responses:
 *       200:
 *         description: 文件上传成功
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 method:
 *                   type: string
 *                   example: POST
 *                 message:
 *                   type: string
 *                   example: File uploaded successfully
 *                 timestamp:
 *                   type: string
 *                   format: date-time
 *                 file:
 *                   type: object
 *                   properties:
 *                     originalname:
 *                       type: string
 *                     filename:
 *                       type: string
 *                     size:
 *                       type: number
 *                     path:
 *                       type: string
 *       400:
 *         description: 没有文件上传
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ErrorResponse'
 */
// 文件上传
app.post('/tpc/upload', upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }
  
  res.json({
    method: 'POST',
    message: 'File uploaded successfully',
    timestamp: new Date().toISOString(),
    file: {
      originalname: req.file.originalname,
      filename: req.file.filename,
      size: req.file.size,
      path: req.file.path
    }
  });
});

/**
 * @swagger
 * /tpc/download:
 *   get:
 *     summary: 文件下载
 *     description: 下载上传目录中的第一个文件
 *     tags: [Files]
 *     responses:
 *       200:
 *         description: 文件下载成功
 *         content:
 *           application/octet-stream:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 没有可下载的文件
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ErrorResponse'
 */
// 文件下载
app.get('/tpc/download', (req, res) => {
  const uploadDir = path.join(__dirname, 'upload');
  
  // 检查上传目录是否存在
  if (!fs.existsSync(uploadDir)) {
    return res.status(404).json({ error: 'Upload directory not found' });
  }
  
  // 获取目录中的文件列表
  const files = fs.readdirSync(uploadDir);
  
  if (files.length === 0) {
    return res.status(404).json({ error: 'No files available for download' });
  }
  
  // 下载第一个文件
  const filePath = path.join(uploadDir, files[0]);
  res.download(filePath, files[0], (err) => {
    if (err) {
      console.error('Download error:', err);
      res.status(500).json({ error: 'Download failed' });
    }
  });
});

/**
 * @swagger
 * /upload/{filename}:
 *   get:
 *     summary: 下载指定文件
 *     description: 下载上传目录中指定文件名的文件
 *     tags: [Files]
 *     parameters:
 *       - in: path
 *         name: filename
 *         required: true
 *         schema:
 *           type: string
 *         description: 要下载的文件名
 *         example: "1732612345678-test.png"
 *     responses:
 *       200:
 *         description: 文件下载成功
 *         content:
 *           application/octet-stream:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 文件不存在
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ErrorResponse'
 *       400:
 *         description: 非法文件路径
 */
// 下载指定文件
app.get('/upload/:filename', (req, res) => {
  const filename = req.params.filename;
  const uploadDir = path.join(__dirname, 'upload');
  const filePath = path.join(uploadDir, filename);
  
  // 安全检查：防止路径遍历攻击
  const resolvedPath = path.resolve(filePath);
  const resolvedUploadDir = path.resolve(uploadDir);
  
  if (!resolvedPath.startsWith(resolvedUploadDir)) {
    return res.status(400).json({ 
      error: 'Invalid file path',
      message: '非法的文件路径'
    });
  }
  
  // 检查文件是否存在
  if (!fs.existsSync(filePath)) {
    return res.status(404).json({ 
      error: 'File not found',
      message: `文件 "${filename}" 不存在`,
      hint: '请先上传文件到 /tpc/upload'
    });
  }
  
  // 获取文件信息
  const stat = fs.statSync(filePath);
  
  // 检查是否是文件（不是目录）
  if (!stat.isFile()) {
    return res.status(400).json({ 
      error: 'Not a file',
      message: '请求的路径不是文件'
    });
  }
  
  // 下载文件
  res.download(filePath, filename, (err) => {
    if (err) {
      console.error('Download error:', err);
      if (!res.headersSent) {
        res.status(500).json({ error: 'Download failed' });
      }
    }
  });
});

/**
 * @swagger
 * /upload:
 *   get:
 *     summary: 列出所有已上传的文件
 *     description: 获取上传目录中所有文件的列表
 *     tags: [Files]
 *     responses:
 *       200:
 *         description: 文件列表
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 message:
 *                   type: string
 *                   example: 已上传的文件列表
 *                 files:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       filename:
 *                         type: string
 *                       size:
 *                         type: number
 *                       downloadUrl:
 *                         type: string
 *                       uploadTime:
 *                         type: string
 */
// 列出所有已上传的文件
app.get('/upload', (req, res) => {
  const uploadDir = path.join(__dirname, 'upload');
  
  // 检查上传目录是否存在
  if (!fs.existsSync(uploadDir)) {
    return res.json({ 
      message: '已上传的文件列表',
      files: [],
      hint: '上传目录为空，请先上传文件到 /tpc/upload'
    });
  }
  
  // 获取目录中的文件列表
  const files = fs.readdirSync(uploadDir);
  
  const fileList = files.map(filename => {
    const filePath = path.join(uploadDir, filename);
    const stat = fs.statSync(filePath);
    return {
      filename: filename,
      size: stat.size,
      downloadUrl: `/upload/${encodeURIComponent(filename)}`,
      uploadTime: stat.mtime.toISOString()
    };
  });
  
  res.json({
    message: '已上传的文件列表',
    count: fileList.length,
    files: fileList
  });
});

// ==================== 文件管理页面 ====================

/**
 * @swagger
 * /files:
 *   get:
 *     summary: 文件管理页面
 *     description: 显示上传文件夹中的所有文件，支持上传和下载
 *     tags: [Files]
 *     responses:
 *       200:
 *         description: HTML 文件管理页面
 *         content:
 *           text/html:
 *             schema:
 *               type: string
 */
// 文件管理页面
app.get('/files', (req, res) => {
  const uploadDir = path.join(__dirname, 'upload');
  let files = [];
  
  // 获取文件列表
  if (fs.existsSync(uploadDir)) {
    const fileNames = fs.readdirSync(uploadDir);
    files = fileNames.map(filename => {
      const filePath = path.join(uploadDir, filename);
      const stat = fs.statSync(filePath);
      return {
        filename: filename,
        size: stat.size,
        sizeFormatted: formatFileSize(stat.size),
        uploadTime: stat.mtime.toISOString(),
        uploadTimeFormatted: stat.mtime.toLocaleString('zh-CN')
      };
    });
  }
  
  // 生成文件列表 HTML
  const fileListHtml = files.length > 0 
    ? files.map(f => `
        <tr>
          <td class="filename" title="${f.filename}">${f.filename}</td>
          <td>${f.sizeFormatted}</td>
          <td>${f.uploadTimeFormatted}</td>
          <td>
            <a href="/upload/${encodeURIComponent(f.filename)}" class="btn btn-download" download>📥 下载</a>
            <button class="btn btn-delete" onclick="deleteFile('${f.filename}')">🗑️ 删除</button>
          </td>
        </tr>
      `).join('')
    : '<tr><td colspan="4" class="empty">📭 暂无文件，请上传</td></tr>';

  res.send(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>文件管理 - OHOS Test Server</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      min-height: 100vh;
      padding: 20px;
    }
    
    .container {
      max-width: 1000px;
      margin: 0 auto;
    }
    
    .header {
      text-align: center;
      color: white;
      margin-bottom: 30px;
    }
    
    .header h1 {
      font-size: 2.5rem;
      margin-bottom: 10px;
      text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
    }
    
    .header p {
      opacity: 0.9;
    }
    
    .card {
      background: white;
      border-radius: 16px;
      box-shadow: 0 10px 40px rgba(0,0,0,0.2);
      overflow: hidden;
      margin-bottom: 20px;
    }
    
    .card-header {
      background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%);
      padding: 20px;
      border-bottom: 1px solid #eee;
    }
    
    .card-header h2 {
      color: #333;
      font-size: 1.3rem;
    }
    
    .card-body {
      padding: 20px;
    }
    
    /* 上传区域 */
    .upload-area {
      border: 3px dashed #ddd;
      border-radius: 12px;
      padding: 40px;
      text-align: center;
      transition: all 0.3s ease;
      cursor: pointer;
      background: #fafafa;
    }
    
    .upload-area:hover, .upload-area.dragover {
      border-color: #667eea;
      background: #f0f4ff;
    }
    
    .upload-area .icon {
      font-size: 4rem;
      margin-bottom: 15px;
    }
    
    .upload-area p {
      color: #666;
      margin-bottom: 15px;
    }
    
    .upload-area input[type="file"] {
      display: none;
    }
    
    .btn {
      display: inline-block;
      padding: 10px 20px;
      border-radius: 8px;
      text-decoration: none;
      font-weight: 500;
      cursor: pointer;
      border: none;
      transition: all 0.3s ease;
      font-size: 14px;
    }
    
    .btn-primary {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }
    
    .btn-primary:hover {
      transform: translateY(-2px);
      box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
    }
    
    .btn-download {
      background: #28a745;
      color: white;
      margin-right: 8px;
    }
    
    .btn-download:hover {
      background: #218838;
    }
    
    .btn-delete {
      background: #dc3545;
      color: white;
    }
    
    .btn-delete:hover {
      background: #c82333;
    }
    
    /* 文件列表表格 */
    .file-table {
      width: 100%;
      border-collapse: collapse;
    }
    
    .file-table th {
      background: #f8f9fa;
      padding: 15px;
      text-align: left;
      font-weight: 600;
      color: #333;
      border-bottom: 2px solid #dee2e6;
    }
    
    .file-table td {
      padding: 15px;
      border-bottom: 1px solid #eee;
      vertical-align: middle;
    }
    
    .file-table tr:hover {
      background: #f8f9fa;
    }
    
    .file-table .filename {
      max-width: 300px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-family: monospace;
      color: #495057;
    }
    
    .file-table .empty {
      text-align: center;
      color: #999;
      padding: 40px;
    }
    
    /* 进度条 */
    .progress-container {
      display: none;
      margin-top: 20px;
    }
    
    .progress-bar {
      height: 8px;
      background: #e9ecef;
      border-radius: 4px;
      overflow: hidden;
    }
    
    .progress-fill {
      height: 100%;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      width: 0%;
      transition: width 0.3s ease;
    }
    
    .progress-text {
      text-align: center;
      margin-top: 10px;
      color: #666;
    }
    
    /* 消息提示 */
    .message {
      padding: 15px 20px;
      border-radius: 8px;
      margin-bottom: 20px;
      display: none;
    }
    
    .message.success {
      background: #d4edda;
      color: #155724;
      border: 1px solid #c3e6cb;
    }
    
    .message.error {
      background: #f8d7da;
      color: #721c24;
      border: 1px solid #f5c6cb;
    }
    
    /* 统计信息 */
    .stats {
      display: flex;
      gap: 20px;
      margin-bottom: 15px;
    }
    
    .stat-item {
      background: #f8f9fa;
      padding: 10px 20px;
      border-radius: 8px;
    }
    
    .stat-item span {
      font-weight: bold;
      color: #667eea;
    }
    
    /* 返回链接 */
    .back-link {
      display: inline-block;
      color: white;
      text-decoration: none;
      margin-bottom: 20px;
      opacity: 0.9;
    }
    
    .back-link:hover {
      opacity: 1;
    }
    
    @media (max-width: 768px) {
      .header h1 {
        font-size: 1.8rem;
      }
      
      .file-table th, .file-table td {
        padding: 10px;
        font-size: 14px;
      }
      
      .btn {
        padding: 8px 12px;
        font-size: 12px;
      }
    }
  </style>
</head>
<body>
  <div class="container">
    <a href="/" class="back-link">← 返回首页</a>
    
    <div class="header">
      <h1>📁 文件管理</h1>
      <p>上传、下载和管理您的文件</p>
    </div>
    
    <div id="message" class="message"></div>
    
    <!-- 上传区域 -->
    <div class="card">
      <div class="card-header">
        <h2>📤 上传文件</h2>
      </div>
      <div class="card-body">
        <div class="upload-area" id="uploadArea">
          <div class="icon">📁</div>
          <p>拖拽文件到此处，或点击选择文件</p>
          <input type="file" id="fileInput" multiple>
          <button class="btn btn-primary" onclick="document.getElementById('fileInput').click()">选择文件</button>
        </div>
        <div class="progress-container" id="progressContainer">
          <div class="progress-bar">
            <div class="progress-fill" id="progressFill"></div>
          </div>
          <div class="progress-text" id="progressText">上传中...</div>
        </div>
      </div>
    </div>
    
    <!-- 文件列表 -->
    <div class="card">
      <div class="card-header">
        <h2>📋 文件列表</h2>
      </div>
      <div class="card-body">
        <div class="stats">
          <div class="stat-item">文件数量: <span>${files.length}</span></div>
          <div class="stat-item">总大小: <span>${formatFileSize(files.reduce((sum, f) => sum + f.size, 0))}</span></div>
        </div>
        <table class="file-table">
          <thead>
            <tr>
              <th>文件名</th>
              <th>大小</th>
              <th>上传时间</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody id="fileList">
            ${fileListHtml}
          </tbody>
        </table>
      </div>
    </div>
  </div>
  
  <script>
    const uploadArea = document.getElementById('uploadArea');
    const fileInput = document.getElementById('fileInput');
    const progressContainer = document.getElementById('progressContainer');
    const progressFill = document.getElementById('progressFill');
    const progressText = document.getElementById('progressText');
    const messageDiv = document.getElementById('message');
    
    // 显示消息
    function showMessage(text, type) {
      messageDiv.textContent = text;
      messageDiv.className = 'message ' + type;
      messageDiv.style.display = 'block';
      setTimeout(() => {
        messageDiv.style.display = 'none';
      }, 5000);
    }
    
    // 拖拽事件
    uploadArea.addEventListener('dragover', (e) => {
      e.preventDefault();
      uploadArea.classList.add('dragover');
    });
    
    uploadArea.addEventListener('dragleave', () => {
      uploadArea.classList.remove('dragover');
    });
    
    uploadArea.addEventListener('drop', (e) => {
      e.preventDefault();
      uploadArea.classList.remove('dragover');
      const files = e.dataTransfer.files;
      if (files.length > 0) {
        uploadFiles(files);
      }
    });
    
    // 文件选择
    fileInput.addEventListener('change', () => {
      if (fileInput.files.length > 0) {
        uploadFiles(fileInput.files);
      }
    });
    
    // 上传文件
    async function uploadFiles(files) {
      for (let i = 0; i < files.length; i++) {
        await uploadFile(files[i], i + 1, files.length);
      }
      // 刷新页面显示新文件
      setTimeout(() => location.reload(), 500);
    }
    
    async function uploadFile(file, current, total) {
      const formData = new FormData();
      formData.append('file', file);
      
      progressContainer.style.display = 'block';
      progressText.textContent = '上传中: ' + file.name + ' (' + current + '/' + total + ')';
      
      try {
        const xhr = new XMLHttpRequest();
        
        xhr.upload.addEventListener('progress', (e) => {
          if (e.lengthComputable) {
            const percent = Math.round((e.loaded / e.total) * 100);
            progressFill.style.width = percent + '%';
          }
        });
        
        await new Promise((resolve, reject) => {
          xhr.onload = () => {
            if (xhr.status === 200) {
              showMessage('文件 "' + file.name + '" 上传成功！', 'success');
              resolve();
            } else {
              reject(new Error('上传失败'));
            }
          };
          xhr.onerror = () => reject(new Error('网络错误'));
          xhr.open('POST', '/tpc/upload');
          xhr.send(formData);
        });
        
      } catch (error) {
        showMessage('上传失败: ' + error.message, 'error');
      }
      
      progressContainer.style.display = 'none';
      progressFill.style.width = '0%';
    }
    
    // 删除文件
    async function deleteFile(filename) {
      if (!confirm('确定要删除文件 "' + filename + '" 吗？')) {
        return;
      }
      
      try {
        const response = await fetch('/upload/' + encodeURIComponent(filename), {
          method: 'DELETE'
        });
        
        if (response.ok) {
          showMessage('文件已删除', 'success');
          setTimeout(() => location.reload(), 500);
        } else {
          const data = await response.json();
          showMessage('删除失败: ' + data.message, 'error');
        }
      } catch (error) {
        showMessage('删除失败: ' + error.message, 'error');
      }
    }
  </script>
</body>
</html>
  `);
});

// 格式化文件大小
function formatFileSize(bytes) {
  if (bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

// 删除文件接口
app.delete('/upload/:filename', (req, res) => {
  const filename = req.params.filename;
  const uploadDir = path.join(__dirname, 'upload');
  const filePath = path.join(uploadDir, filename);
  
  // 安全检查：防止路径遍历攻击
  const resolvedPath = path.resolve(filePath);
  const resolvedUploadDir = path.resolve(uploadDir);
  
  if (!resolvedPath.startsWith(resolvedUploadDir)) {
    return res.status(400).json({ 
      error: 'Invalid file path',
      message: '非法的文件路径'
    });
  }
  
  // 检查文件是否存在
  if (!fs.existsSync(filePath)) {
    return res.status(404).json({ 
      error: 'File not found',
      message: `文件 "${filename}" 不存在`
    });
  }
  
  try {
    fs.unlinkSync(filePath);
    res.json({ 
      message: '文件删除成功',
      filename: filename
    });
  } catch (error) {
    res.status(500).json({ 
      error: 'Delete failed',
      message: '删除失败: ' + error.message
    });
  }
});

// ==================== 证书下载 API ====================

/**
 * @swagger
 * /cert/ca.crt:
 *   get:
 *     summary: 下载 CA 根证书
 *     description: 下载 CA 根证书用于客户端信任服务器（自签名证书）
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: CA 证书文件
 *         content:
 *           application/x-x509-ca-cert:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 证书不存在
 *         content:
 *           application/json:
 *             schema:
 *               $ref: '#/components/schemas/ErrorResponse'
 */
// 获取 CA 证书（服务器证书即为自签名 CA）
app.get('/cert/ca.crt', (req, res) => {
  const certPath = path.join(__dirname, '../../cert/server.crt');
  
  if (!fs.existsSync(certPath)) {
    return res.status(404).json({ 
      error: 'CA certificate not found',
      message: '证书文件不存在，请先运行服务器生成证书'
    });
  }
  
  res.setHeader('Content-Type', 'application/x-x509-ca-cert');
  res.setHeader('Content-Disposition', 'attachment; filename="ca.crt"');
  res.sendFile(certPath);
});

/**
 * @swagger
 * /cert/server.crt:
 *   get:
 *     summary: 下载服务器证书
 *     description: 下载服务器 SSL/TLS 证书
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 服务器证书文件
 *         content:
 *           application/x-x509-ca-cert:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 证书不存在
 */
// 获取服务器证书
app.get('/cert/server.crt', (req, res) => {
  const certPath = path.join(__dirname, '../../cert/server.crt');
  
  if (!fs.existsSync(certPath)) {
    return res.status(404).json({ 
      error: 'Server certificate not found',
      message: '证书文件不存在'
    });
  }
  
  res.setHeader('Content-Type', 'application/x-x509-ca-cert');
  res.setHeader('Content-Disposition', 'attachment; filename="server.crt"');
  res.sendFile(certPath);
});

/**
 * @swagger
 * /cert/client.crt:
 *   get:
 *     summary: 下载客户端证书
 *     description: 下载客户端证书用于双向认证
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 客户端证书文件
 *         content:
 *           application/x-x509-ca-cert:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 证书不存在，需要先生成
 */
// 获取客户端证书（如果存在）
app.get('/cert/client.crt', (req, res) => {
  const certPath = path.join(__dirname, '../../cert/client.crt');
  
  if (!fs.existsSync(certPath)) {
    return res.status(404).json({ 
      error: 'Client certificate not found',
      message: '客户端证书不存在，请运行 generate_test_certs.sh 生成'
    });
  }
  
  res.setHeader('Content-Type', 'application/x-x509-ca-cert');
  res.setHeader('Content-Disposition', 'attachment; filename="client.crt"');
  res.sendFile(certPath);
});

/**
 * @swagger
 * /cert/client.key:
 *   get:
 *     summary: 下载客户端私钥
 *     description: 下载客户端私钥（无密码保护，仅用于测试）
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 客户端私钥文件
 *         content:
 *           application/x-pem-file:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 私钥不存在
 */
// 获取客户端私钥（无密码版本 - 测试用）
app.get('/cert/client.key', (req, res) => {
  const keyPath = path.join(__dirname, '../../cert/client.key');
  
  if (!fs.existsSync(keyPath)) {
    return res.status(404).json({ 
      error: 'Client key not found',
      message: '客户端私钥不存在，请运行 generate_test_certs.sh 生成'
    });
  }
  
  res.setHeader('Content-Type', 'application/x-pem-file');
  res.setHeader('Content-Disposition', 'attachment; filename="client.key"');
  res.sendFile(keyPath);
});

/**
 * @swagger
 * /cert/client_encrypted.key:
 *   get:
 *     summary: 下载加密客户端私钥
 *     description: 下载带密码保护的客户端私钥（密码：123456）
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 加密私钥文件
 *         content:
 *           application/x-pem-file:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 加密私钥不存在
 */
// 获取客户端加密私钥（带密码保护）
app.get('/cert/client_encrypted.key', (req, res) => {
  const keyPath = path.join(__dirname, '../../cert/client_encrypted.key');
  
  if (!fs.existsSync(keyPath)) {
    return res.status(404).json({ 
      error: 'Encrypted client key not found',
      message: '加密的客户端私钥不存在'
    });
  }
  
  res.setHeader('Content-Type', 'application/x-pem-file');
  res.setHeader('Content-Disposition', 'attachment; filename="client_encrypted.key"');
  res.sendFile(keyPath);
});

/**
 * @swagger
 * /cert/client.p12:
 *   get:
 *     summary: 下载客户端 PKCS#12 证书包
 *     description: |
 *       下载 PKCS#12 格式的客户端证书包（.p12/.pfx），包含客户端证书和私钥。
 *       可直接导入到浏览器、移动设备或应用程序中。
 *       
 *       **默认无密码**，可通过 password 参数设置密码
 *       
 *       **使用场景**:
 *       - 浏览器证书导入
 *       - iOS/Android 应用
 *       - OpenHarmony 应用
 *     tags: [Certificates]
 *     parameters:
 *       - in: query
 *         name: password
 *         schema:
 *           type: string
 *         description: 自定义 P12 密码（可选，默认无密码）
 *         example: "mypassword"
 *     responses:
 *       200:
 *         description: PKCS#12 证书包文件
 *         headers:
 *           X-P12-Password:
 *             schema:
 *               type: string
 *             description: P12 文件的密码（空字符串表示无密码）
 *         content:
 *           application/x-pkcs12:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 证书不存在
 *       500:
 *         description: 生成 P12 文件失败
 */
// 获取客户端 P12 证书包
app.get('/cert/client.p12', (req, res) => {
  const certPath = path.join(__dirname, '../../cert/client.crt');
  const keyPath = path.join(__dirname, '../../cert/client.key');
  const p12Path = path.join(__dirname, '../../cert/client.p12');
  const p12NoPassPath = path.join(__dirname, '../../cert/client_nopass.p12');
  
  // 检查证书和私钥是否存在
  if (!fs.existsSync(certPath) || !fs.existsSync(keyPath)) {
    return res.status(404).json({ 
      error: 'Client certificate or key not found',
      message: '客户端证书或私钥不存在',
      hint: '请运行 ./generate_test_certs.sh 生成证书'
    });
  }
  
  // 获取自定义密码，不传则为空（无密码）
  const password = req.query.password || '';
  const hasPassword = !!req.query.password;
  
  // 如果无密码版本 P12 文件已存在且没有自定义密码，直接返回
  if (fs.existsSync(p12NoPassPath) && !hasPassword) {
    res.setHeader('Content-Type', 'application/x-pkcs12');
    res.setHeader('Content-Disposition', 'attachment; filename="client.p12"');
    res.setHeader('X-P12-Password', '');
    return res.sendFile(p12NoPassPath);
  }
  
  // 生成 P12 文件
  const { execSync } = require('child_process');
  
  try {
    // 使用 openssl 将证书和私钥打包成 P12 格式
    const tempP12 = hasPassword ? 
      path.join(__dirname, `../../cert/client_${Date.now()}.p12`) : 
      p12NoPassPath;
    
    // 检查是否存在 CA 证书
    const caPath = path.join(__dirname, '../../cert/ca.crt');
    const hasCaCert = fs.existsSync(caPath);
    
    // 构建 openssl 命令（包含 CA 证书链）
    let command = `openssl pkcs12 -export -out "${tempP12}" -inkey "${keyPath}" -in "${certPath}"`;
    if (hasCaCert) {
      command += ` -certfile "${caPath}"`;
    }
    // 使用 -passout pass: 设置密码，空密码时为 -passout pass:
    command += ` -passout pass:${password}`;
    
    execSync(command, { stdio: 'pipe' });
    
    // 发送文件
    res.setHeader('Content-Type', 'application/x-pkcs12');
    res.setHeader('Content-Disposition', 'attachment; filename="client.p12"');
    res.setHeader('X-P12-Password', password);
    
    res.sendFile(tempP12, (err) => {
      // 如果使用了临时文件（自定义密码），发送后删除
      if (hasPassword && fs.existsSync(tempP12)) {
        try {
          fs.unlinkSync(tempP12);
        } catch (e) {
          console.error('Failed to delete temp P12 file:', e);
        }
      }
      
      if (err) {
        console.error('Error sending P12 file:', err);
      }
    });
    
  } catch (error) {
    console.error('Error generating P12 file:', error);
    res.status(500).json({ 
      error: 'Failed to generate P12 file',
      message: 'P12 文件生成失败',
      details: error.message,
      hint: '请确保已安装 OpenSSL'
    });
  }
});

/**
 * @swagger
 * /cert/client_encrypted.p12:
 *   get:
 *     summary: 下载带密码的客户端 PKCS#12 证书包
 *     description: |
 *       下载带密码保护的 PKCS#12 格式客户端证书包（.p12/.pfx）。
 *       **专用于 4002 端口的双向认证测试**。
 *       
 *       **固定密码**: `123456`
 *       
 *       **使用场景**:
 *       - 测试带密码的客户端证书
 *       - 4002 端口双向认证
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 带密码的 PKCS#12 证书包文件
 *         headers:
 *           X-P12-Password:
 *             schema:
 *               type: string
 *             description: P12 文件的密码（固定为 123456）
 *         content:
 *           application/x-pkcs12:
 *             schema:
 *               type: string
 *               format: binary
 *       404:
 *         description: 证书不存在
 *       500:
 *         description: 生成 P12 文件失败
 */
// 获取带密码的客户端 P12 证书包（专用于 4002 端口，密码: 123456）
app.get('/cert/client_encrypted.p12', (req, res) => {
  const certPath = path.join(__dirname, '../../cert/client.crt');
  const encryptedKeyPath = path.join(__dirname, '../../cert/client_encrypted.key');
  const p12EncryptedPath = path.join(__dirname, '../../cert/client_encrypted.p12');
  
  // 检查证书和加密私钥是否存在
  if (!fs.existsSync(certPath) || !fs.existsSync(encryptedKeyPath)) {
    return res.status(404).json({ 
      error: 'Client certificate or encrypted key not found',
      message: '客户端证书或加密私钥不存在',
      hint: '请运行 ./generate_test_certs.sh 或 ./cloud-deploy.sh 生成证书'
    });
  }
  
  // 固定密码为 123456
  const password = '123456';
  
  // 如果带密码的 P12 文件已存在，直接返回
  if (fs.existsSync(p12EncryptedPath)) {
    res.setHeader('Content-Type', 'application/x-pkcs12');
    res.setHeader('Content-Disposition', 'attachment; filename="client_encrypted.p12"');
    res.setHeader('X-P12-Password', password);
    return res.sendFile(p12EncryptedPath);
  }
  
  // 生成带密码的 P12 文件
  const { execSync } = require('child_process');
  
  try {
    // 检查是否存在 CA 证书
    const caPath = path.join(__dirname, '../../cert/ca.crt');
    const hasCaCert = fs.existsSync(caPath);
    
    // 构建 openssl 命令
    // 使用加密的私钥 client_encrypted.key，需要 -passin 读取私钥密码
    let command = `openssl pkcs12 -export -out "${p12EncryptedPath}" -inkey "${encryptedKeyPath}" -passin pass:${password} -in "${certPath}"`;
    if (hasCaCert) {
      command += ` -certfile "${caPath}"`;
    }
    command += ` -passout pass:${password}`;
    
    execSync(command, { stdio: 'pipe' });
    
    // 发送文件
    res.setHeader('Content-Type', 'application/x-pkcs12');
    res.setHeader('Content-Disposition', 'attachment; filename="client_encrypted.p12"');
    res.setHeader('X-P12-Password', password);
    res.sendFile(p12EncryptedPath);
    
  } catch (error) {
    console.error('Error generating encrypted P12 file:', error);
    res.status(500).json({ 
      error: 'Failed to generate encrypted P12 file',
      message: '带密码的 P12 文件生成失败',
      details: error.message,
      hint: '请确保已安装 OpenSSL 且 client_encrypted.key 存在'
    });
  }
});

/**
 * @swagger
 * /cert/client-bundle:
 *   get:
 *     summary: 一键获取所有证书
 *     description: 获取客户端双向认证所需的所有证书（JSON格式）
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 证书包（包含 ca.crt, client.crt, client.key）
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 message:
 *                   type: string
 *                   example: 客户端证书包（双向认证所需）
 *                 files:
 *                   type: object
 *                   properties:
 *                     ca_crt:
 *                       type: string
 *                       description: CA证书内容
 *                     client_crt:
 *                       type: string
 *                       description: 客户端证书内容
 *                     client_key:
 *                       type: string
 *                       description: 客户端私钥内容
 *                 usage:
 *                   type: object
 *                 download_urls:
 *                   type: object
 *                 security_note:
 *                   type: string
 *       404:
 *         description: 部分证书缺失
 */
// 一键下载客户端所需的所有证书（打包）
app.get('/cert/client-bundle', (req, res) => {
  const certDir = path.join(__dirname, '../../cert');
  const files = {
    'ca.crt': path.join(certDir, 'server.crt'),
    'client.crt': path.join(certDir, 'client.crt'),
    'client.key': path.join(certDir, 'client.key')
  };
  
  // 检查所有文件是否存在
  const missing = [];
  for (const [name, filePath] of Object.entries(files)) {
    if (!fs.existsSync(filePath)) {
      missing.push(name);
    }
  }
  
  if (missing.length > 0) {
    return res.status(404).json({ 
      error: 'Some certificates are missing',
      message: '部分证书文件不存在',
      missing: missing,
      hint: '请运行 ./generate_test_certs.sh 生成完整证书'
    });
  }
  
  // 返回 JSON 格式的证书内容
  res.json({
    message: '客户端证书包（双向认证所需）',
    files: {
      ca_crt: fs.readFileSync(files['ca.crt'], 'utf8'),
      client_crt: fs.readFileSync(files['client.crt'], 'utf8'),
      client_key: fs.readFileSync(files['client.key'], 'utf8')
    },
    usage: {
      ca_crt: '用于验证服务器证书',
      client_crt: '客户端证书（双向认证）',
      client_key: '客户端私钥（双向认证）'
    },
    download_urls: {
      ca_crt: '/cert/ca.crt',
      client_crt: '/cert/client.crt',
      client_key: '/cert/client.key'
    },
    security_note: '⚠️ 私钥文件请妥善保管，不要分享给他人'
  });
});

/**
 * @swagger
 * /cert/list:
 *   get:
 *     summary: 查看证书列表
 *     description: 获取所有证书文件的列表和状态信息
 *     tags: [Certificates]
 *     responses:
 *       200:
 *         description: 证书列表
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 message:
 *                   type: string
 *                   example: 证书文件列表
 *                 baseUrl:
 *                   type: string
 *                   example: http://localhost:4000
 *                 certificates:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       filename:
 *                         type: string
 *                       exists:
 *                         type: boolean
 *                       downloadUrl:
 *                         type: string
 *                       size:
 *                         type: number
 *                       modified:
 *                         type: string
 *                         format: date-time
 *                 note:
 *                   type: string
 */
// 证书列表和信息
app.get('/cert/list', (req, res) => {
  const certDir = path.join(__dirname, '../../cert');
  const certFiles = ['server.crt', 'server.key', 'client.crt', 'client.key', 'client_encrypted.key'];
  
  const certInfo = certFiles.map(file => {
    const filePath = path.join(certDir, file);
    const exists = fs.existsSync(filePath);
    let info = {
      filename: file,
      exists: exists,
      downloadUrl: exists ? `/cert/${file}` : null
    };
    
    if (exists) {
      const stats = fs.statSync(filePath);
      info.size = stats.size;
      info.modified = stats.mtime.toISOString();
    }
    
    return info;
  });
  
  res.json({
    message: '证书文件列表',
    baseUrl: `http://${req.headers.host}`,
    certificates: certInfo,
    note: 'server.crt 同时作为自签名 CA 证书使用'
  });
});

// ==================== 服务器启动 ====================

// 1. HTTP 服务器 (端口 4000)
const httpServer = http.createServer(app);
httpServer.listen(4000, () => {
  console.log('✅ HTTP Server running on http://0.0.0.0:4000');
});

// 2. HTTPS 基础服务器 (端口 4001)
try {
  const httpsOptions1 = {
    key: fs.readFileSync(path.join(__dirname, '../../cert/server.key')),
    cert: fs.readFileSync(path.join(__dirname, '../../cert/server.crt'))
  };
  const httpsServer1 = https.createServer(httpsOptions1, app);
  httpsServer1.listen(4001, () => {
    console.log('✅ HTTPS Server (basic) running on https://0.0.0.0:4001');
  });
} catch (err) {
  console.error('❌ Failed to start HTTPS Server (4001):', err.message);
}

// 3. HTTPS 双向认证 (端口 4002)
// 注意：这里验证客户端证书，但客户端私钥密码是在客户端配置的，不是服务器端
try {
  const httpsOptions2 = {
    key: fs.readFileSync(path.join(__dirname, '../../cert/server.key')),
    cert: fs.readFileSync(path.join(__dirname, '../../cert/server.crt')),
    ca: fs.readFileSync(path.join(__dirname, '../../cert/ca.crt')),
    requestCert: true,       // 要求客户端提供证书
    rejectUnauthorized: true // 拒绝无效的客户端证书
  };
  const httpsServer2 = https.createServer(httpsOptions2, app);
  httpsServer2.listen(4002, () => {
    console.log('✅ HTTPS Server (mutual TLS) running on https://0.0.0.0:4002');
  });
} catch (err) {
  console.error('❌ Failed to start HTTPS Server (4002):', err.message);
}

// 4. HTTPS 双向认证无密码 (端口 4003)
try {
  const httpsOptions3 = {
    key: fs.readFileSync(path.join(__dirname, '../../cert/server.key')),
    cert: fs.readFileSync(path.join(__dirname, '../../cert/server.crt')),
    ca: fs.readFileSync(path.join(__dirname, '../../cert/ca.crt')),
    requestCert: true,
    rejectUnauthorized: true
  };
  const httpsServer3 = https.createServer(httpsOptions3, app);
  httpsServer3.listen(4003, () => {
    console.log('✅ HTTPS Server (mutual auth no password) running on https://0.0.0.0:4003');
  });
} catch (err) {
  console.error('❌ Failed to start HTTPS Server (4003):', err.message);
}

// 5. HTTPS TLS 1.2 (端口 4004)
try {
  const httpsOptions4 = {
    key: fs.readFileSync(path.join(__dirname, '../../cert/server.key')),
    cert: fs.readFileSync(path.join(__dirname, '../../cert/server.crt')),
    minVersion: 'TLSv1.2',
    maxVersion: 'TLSv1.2'
  };
  const httpsServer4 = https.createServer(httpsOptions4, app);
  httpsServer4.listen(4004, () => {
    console.log('✅ HTTPS Server (TLS 1.2) running on https://0.0.0.0:4004');
  });
} catch (err) {
  console.error('❌ Failed to start HTTPS Server (4004):', err.message);
}

// 6. HTTP/2 over TLS (端口 4005)
try {
  const http2Options = {
    key: fs.readFileSync(path.join(__dirname, '../../cert/server.key')),
    cert: fs.readFileSync(path.join(__dirname, '../../cert/server.crt')),
    allowHTTP1: true // 允许降级到 HTTP/1.1
  };
  const http2Server = http2.createSecureServer(http2Options, app);
  http2Server.listen(4005, () => {
    console.log('✅ HTTP/2 Server running on https://0.0.0.0:4005');
  });
} catch (err) {
  console.error('❌ Failed to start HTTP/2 Server (4005):', err.message);
}

// 错误处理
process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

console.log('🚀 OHOS Axios Test Server Started');
console.log('📝 Note: Some HTTPS servers may fail if certificates are not available');
